//--Yangxin--

`include "defines.v"
//csr 寄存器模块
module csr_reg(
	input wire         clk              ,
	input wire         reset            ,
	//from wb
	input wire         we               ,
	input wire  [63:0] raddr            ,
	input wire  [63:0] waddr            ,
	input wire  [63:0] wdata            ,
	output wire [63:0] rdata            ,
	
	output wire        global_int_en_o  ,

	//ecall & mret
	input  wire [`WS_EXCEPT_BUS_WD-1:0] csr_except_ctrl  , 
	//difftest
	output wire [63:0] csr_mcycle_o     ,
	output wire [63:0] csr_mtvec_o      , 
	output wire [63:0] csr_mcause_o     ,
	output wire [63:0] csr_mepc_o       , 
	output wire [63:0] csr_mstatus_o    ,
	output wire [63:0] csr_mie_o        ,
	output wire [63:0] csr_mip_o        ,
	output wire [63:0] csr_mscratch_o   , 
	output wire [63:0] csr_sstatus_o    , 

	//csr_regs
	output wire [63:0] csr_mtvec_to_ws  ,
	output wire [63:0] csr_mepc_to_ws   ,
	output wire [63:0] csr_mstatus_to_ws,
	output wire        csr_mie_mtie_to_ws    
	);

localparam CSR_MCYCLE   = 12'hb00;
localparam CSR_MTVEC    = 12'h305;
localparam CSR_MCAUSE   = 12'h342;
localparam CSR_MEPC     = 12'h341;
localparam CSR_MSTATUS  = 12'h300;
localparam CSR_MIE      = 12'h304;
localparam CSR_MIP      = 12'h344;
localparam CSR_MSCRATCH = 12'h340;

reg [63:0] mcycle ;  //machine cycle counter
reg [63:0] mtvec  ;
reg [63:0] mcause ;
reg [63:0] mepc   ;
reg [63:0] mstatus;
reg [63:0] mie    ;
reg [63:0] mip    ;
reg [63:0] mscratch;

assign global_int_en_o = (mstatus[3] == 1'b1) ? 1'b1 : 1'b0;

//exception ecall and mret
wire        except_ecall      ;
wire        except_mret       ;
wire [63:0] except_pc         ; 
wire        except_status_mie ;
wire 	    except_status_mpie; 
wire        except_timer_interrupt;
wire [63:0] except_int_next_pc;

assign {except_int_next_pc    ,//132:69
		except_timer_interrupt,//68:68
		except_ecall      ,    //67:67
		except_mret       ,    //66:66
		except_pc         ,    //65:2
		except_status_mie ,    //1:1
		except_status_mpie     //0:0
		} = csr_except_ctrl;


//cycle counter
always @(posedge clk) begin
	if (reset) begin
		// reset
		mcycle <= 64'h0;
	end
	else begin
		mcycle <= mcycle + 1'b1;
	end
end

//WRITE CSR
//mtvec
always @(posedge clk) begin
	if (reset) begin
		// reset
		mtvec <= 64'h0;
	end
	else if (we && (waddr == CSR_MTVEC)) begin
		mtvec <= wdata;
	end
end

//mcause
always @(posedge clk) begin
	if (reset) begin
		// reset
		mcause <= 64'h0;
	end
	else if(except_timer_interrupt) begin
		mcause <= 64'h80000000_00000007;
	end
	else if(except_ecall) begin
	    mcause <= 64'd11;
	end
	else if (we && (waddr == CSR_MCAUSE)) begin
		mcause <= wdata;
	end
end

//mepc
always @(posedge clk) begin
	if (reset) begin
		// reset
		mepc <= 64'h0;
	end
	else if(except_timer_interrupt) begin
		//mepc <= except_pc;
		mepc <= except_int_next_pc;
	end
	else if(except_ecall) begin
		mepc <= except_pc;
	end
	else if (we && (waddr == CSR_MEPC)) begin
		mepc <= wdata;
	end
end

//mstatus
always @(posedge clk) begin
	if (reset) begin
		// reset
		mstatus <= 64'h0;
	end
	else if(except_timer_interrupt) begin
		mstatus[7] <= except_status_mie;
		mstatus[3] <= 1'b0;
		mstatus[12:11] <= 2'b11;         //????
	end
	else if(except_mret) begin
		mstatus[3] <= except_status_mpie;
		mstatus[7] <= 1'b1;
		mstatus[12:11] <= 2'b0;
	end
	else if(except_ecall) begin
		mstatus[7] <= except_status_mie;
		mstatus[3] <= 1'b0;
		mstatus[12:11] <= 2'b11;
	end
	else if (we && (waddr == CSR_MSTATUS)) begin
		mstatus <= {((wdata[16:15]==2'b11) || (wdata[14:13] == 2'b11)) ,wdata[62:0]};
	end
end

//mie
always @(posedge clk) begin
	if (reset) begin
		// reset
		mie <= 64'h0;
	end
	else if (we && (waddr == CSR_MIE)) begin
		mie <= wdata;
	end
end

//mip
always @(posedge clk) begin
	if (reset) begin
		// reset
		mip <= 64'h0;
	end
	else if(except_timer_interrupt) begin
		mip[7] <= 1'b1;
	end
	else if (we && (waddr == CSR_MIP)) begin
		mip <= wdata;
	end
end

//mscratch
always @(posedge clk) begin
	if (reset) begin
		// reset
		mscratch <= 64'h0;
	end
	else if (we && (waddr == CSR_MSCRATCH)) begin
		mscratch <= wdata;
	end
end

//READ
assign rdata = (raddr[11:0] == waddr[11:0] && we) ? wdata  :
               (raddr[11:0] == CSR_MCYCLE      ) ? mcycle :
               (raddr[11:0] == CSR_MTVEC       ) ? mtvec  :
               (raddr[11:0] == CSR_MCAUSE      ) ? mcause :
               (raddr[11:0] == CSR_MEPC        ) ? mepc   :
               (raddr[11:0] == CSR_MSTATUS     ) ? mstatus:
               (raddr[11:0] == CSR_MIE         ) ? mie    :
			   (raddr[11:0] == CSR_MSCRATCH    ) ? mscratch    :
               									    64'h0  ;


//difftest
assign csr_mcycle_o  = (we & waddr[11:0] == CSR_MCYCLE ) ? wdata : mcycle ;
assign csr_mtvec_o   = (we & waddr[11:0] == CSR_MTVEC  ) ? wdata : mtvec  ;
assign csr_mcause_o  = //(except_timer_interrupt         ) ? mcause         :
					   (except_timer_interrupt         ) ? 64'h80000000_00000007:
					   (except_ecall                   ) ? 64'd11         :
                       (we & waddr[11:0] == CSR_MCAUSE ) ? wdata : mcause ;
assign csr_mepc_o    = //(except_timer_interrupt         ) ? mepc           :
					   (except_timer_interrupt         ) ? except_int_next_pc:
					   (except_ecall                   ) ? except_pc      :
					   (we & waddr[11:0] == CSR_MEPC   ) ? wdata : mepc   ;
assign csr_mstatus_o = //(except_timer_interrupt         ) ?
					   //{mstatus[63:13],2'b11,mstatus[10:8],except_status_mie,mstatus[6:4],1'b0,mstatus[2:0]} : 
					   //mstatus: 
					   (except_timer_interrupt         ) ?
					   {mstatus[63:13],2'b11,mstatus[10:8],except_status_mie,mstatus[6:4],1'b0,mstatus[2:0]} : 
					   //{mstatus[63:13],mstatus[12:8],except_status_mie,mstatus[6:4],1'b0,mstatus[2:0]} : 
					   (except_mret                    ) ? 
					   {mstatus[63:13],2'b0,mstatus[10:8],1'b1,mstatus[6:4],except_status_mpie,mstatus[2:0]} : 
					   (except_ecall                   ) ?
					   {mstatus[63:13],2'b11,mstatus[10:8],except_status_mie,mstatus[6:4],1'b0,mstatus[2:0]} : 
					   (we & waddr[11:0] == CSR_MSTATUS) ? {((wdata[16:15]==2'b11) || (wdata[14:13] == 2'b11)) ,wdata[62:0]} : mstatus;

assign csr_mie_o     = (we & waddr[11:0] == CSR_MIE    ) ? wdata : mie    ;
assign csr_mip_o     = (except_timer_interrupt)          ? {mip[63:8],1'b1,mip[6:0]}:
					   (we & waddr[11:0] == CSR_MIP    ) ? wdata : mip    ;
assign csr_mscratch_o     = (we & waddr[11:0] == CSR_MSCRATCH    ) ? wdata : mscratch    ;
assign csr_sstatus_o = csr_mstatus_o & 64'h80000003000DE122;

assign csr_mtvec_to_ws    = mtvec  ;
assign csr_mepc_to_ws     = mepc   ;
assign csr_mstatus_to_ws  = mstatus;
assign csr_mie_mtie_to_ws = mie[7] ;

endmodule
